"""Database connection and management for the SwiftShadow application.

This module provides a singleton `Database` class to handle asynchronous database
operations using SQLAlchemy, including initialization, table creation, session management,
and global configuration handling.
"""

from pathlib import Path
from typing import Optional

from loguru import logger
from sqlalchemy import select
from sqlalchemy.ext.asyncio import (
    AsyncEngine,
    AsyncSession,
    async_sessionmaker,
    create_async_engine,
)

from .task import Base, GlobalConfig


class Database:
    """Manages the database connection and sessions as a singleton.

    This class ensures that only one database instance is used throughout the application,
    providing methods for initialization, table creation, session management, and closing
    the database connection.
    """

    _instance: Optional["Database"] = None
    _engine: AsyncEngine | None = None
    _session_factory: async_sessionmaker[AsyncSession] | None = None

    def __new__(cls) -> "Database":
        """Ensures that only one instance of the Database class is created (singleton pattern).

        Returns:
            The singleton instance of the Database.
        """
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    async def initialize(self, db_url: str | None = None) -> None:
        """Initializes the database connection and creates tables if they don't exist.

        If `db_url` is not provided, it defaults to a SQLite database in the user's
        home directory (`~/.swiftshadow/swiftshadow.db`).

        Args:
            db_url: The database URL string (e.g., 'sqlite+aiosqlite:///./test.db').
        """
        if self._engine is not None:
            logger.warning("数据库已初始化,跳过重复初始化")
            return

        if db_url is None:
            # 默认使用用户目录下的SQLite数据库
            db_dir = Path.home() / ".swiftshadow"
            db_dir.mkdir(parents=True, exist_ok=True)
            db_path = db_dir / "swiftshadow.db"
            db_url = f"sqlite+aiosqlite:///{db_path}"

        logger.info(f"初始化数据库: {db_url}")

        # 创建异步引擎
        self._engine = create_async_engine(
            db_url,
            echo=False,  # 生产环境关闭SQL日志
            future=True,
            pool_pre_ping=True,  # 连接池健康检查
            pool_size=5,
            max_overflow=10,
        )

        # 创建会话工厂
        self._session_factory = async_sessionmaker(
            bind=self._engine,
            class_=AsyncSession,
            expire_on_commit=False,
        )

        # 创建所有表
        await self.create_tables()

        # 初始化全局配置
        await self._init_global_config()

        logger.success("数据库初始化完成")

    async def create_tables(self) -> None:
        """Creates all defined database tables based on the SQLAlchemy metadata.

        Raises:
            RuntimeError: If the database engine has not been initialized.
        """
        if self._engine is None:
            raise RuntimeError("数据库未初始化")

        async with self._engine.begin() as conn:
            await conn.run_sync(Base.metadata.create_all)
            logger.debug("数据表创建完成")

    async def _init_global_config(self) -> None:
        """Initializes the global configuration in the database if it doesn't already exist.

        A default `GlobalConfig` entry with `id=1` is created if no configuration is found.
        """
        async with self.session() as session:
            result = await session.execute(select(GlobalConfig).where(GlobalConfig.id == 1))
            config = result.scalar_one_or_none()

            if config is None:
                config = GlobalConfig(id=1)
                session.add(config)
                await session.commit()
                logger.info("创建默认全局配置")

    def session(self) -> AsyncSession:
        """Provides an asynchronous database session context manager.

        Raises:
            RuntimeError: If the database has not been initialized.

        Returns:
            An asynchronous session context manager.

        Usage:
            >>> async with db.session() as session:
            ...     result = await session.execute(select(Task))
        """
        if self._session_factory is None:
            raise RuntimeError("数据库未初始化")

        return self._session_factory()

    async def close(self) -> None:
        """Closes the database connection and disposes of the engine.

        This method should be called when the application is shutting down
        to release database resources.
        """
        if self._engine is not None:
            await self._engine.dispose()
            self._engine = None
            self._session_factory = None
            logger.info("数据库连接已关闭")

    @property
    def engine(self) -> AsyncEngine:
        """Gets the asynchronous database engine instance.

        Raises:
            RuntimeError: If the database engine has not been initialized.

        Returns:
            The SQLAlchemy asynchronous engine instance.
        """
        if self._engine is None:
            raise RuntimeError("数据库未初始化")
        return self._engine


# 全局数据库实例
db = Database()
